﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data.Objects.DataClasses;
using System.Data;
using System.Data.Objects;
using System.Reflection;
using System.Data.SqlClient;
using System.Linq.Expressions;
using System.Web;
using System.Runtime.Serialization.Formatters.Binary;
using System.IO;
using System.Collections;

namespace IRC.IRepairClouds.DataLayer
{
    public static class Extensions
    {
        public static string TrimNull(this string str)
        {
            if (str == null || str.Length == 0)
                return null;
            string trim = str.Trim();
            if (trim.Length == 0)
                return null;
            return trim;
        }

        public static DateTime? TrimDateNull(this DateTime? date)
        {
            if (date == null)
                return null;
            return date.Value.Date;
        }

        private static DateTime MinValue = new DateTime(1900, 1, 1);

        public static DateTime? ToNullable(this DateTime date)
        {
            if (date == DateTime.MinValue)
                return null;
            if (date == System.Data.SqlTypes.SqlDateTime.MinValue)
                return null;
            if (date == MinValue)
                return null;
            return date;
        }
        public static DateTime? ToNullable(this DateTime? date)
        {
            if (date != null)
                return ToNullable(date.Value);
            return date;
        }

        public static string ToStringN(this object obj)
        {
            if (obj == null)
                return null;
            return obj.ToString();
        }

        public static Guid? ToGuidN(this string str)
        {
            return ToGuidN(str, false);
        }

        public static Guid? ToGuidN(this string str, bool throwOnFormat)
        {
            if (!string.IsNullOrEmpty(str)) {
                try {
                    return new Guid(str);
                }
                catch { }
                if (throwOnFormat)
                    throw new FormatException();
            }
            return null;
        }

        public static Guid ToGuid(this string str)
        {
            if (!string.IsNullOrEmpty(str)) {
                try {
                    return new Guid(str);
                }
                catch { }
            }
            throw new FormatException();
        }

        public static int? ToIntN(this string str)
        {
            if (!string.IsNullOrEmpty(str)) {
                int res;
                if (int.TryParse(str, out res))
                    return res;
            }
            return null;
        }

        public static int ToInt(this string str)
        {
            return int.Parse(str);
        }

        public static bool? ToBoolN(this string str)
        {
            if (!string.IsNullOrEmpty(str)) {
                bool res;
                if (bool.TryParse(str, out res))
                    return res;
            }
            return null;
        }

        public static bool ToBool(this string str)
        {
            return bool.Parse(str);
        }



        public static string ToLowerN(this string str)
        {
            if (str != null)
                return str.ToLower();
            return str;
        }

        public static string ToLowerInvariantN(this string str)
        {
            if (str != null)
                return str.ToLowerInvariant();
            return str;
        }


        public static bool Flag(this int flag)
        {
            return flag == 1;
        }
        public static bool Flag(this short flag)
        {
            return flag == 1;
        }

        public static int Flag(this bool flag)
        {
            return flag ? 1 : 0;
        }

        public static bool Flag(this int? flag, bool ifNull)
        {
            if (flag == null)
                return ifNull;
            return flag.Value.Flag();
        }
        public static bool? Flag(this int? flag)
        {
            if (flag == null)
                return null;
            return flag.Value.Flag();
        }

        public static bool Flag(this short? flag, bool ifNull)
        {
            if (flag == null)
                return ifNull;
            return flag.Value.Flag();
        }
        public static bool? Flag(this short? flag)
        {
            if (flag == null)
                return null;
            return flag.Value.Flag();
        }

        public static int? Flag(this bool? flag)
        {
            if (flag == null)
                return null;
            return flag.Value.Flag();
        }

        public static string DQuote(this string str)
        {
            return "\"" + str + "\"";
        }
        public static string Quote(this string str)
        {
            return "'" + str + "'";
        }

        public static object DBNullable<T>(this T obj)
        {
            if (obj == null)
                return DBNull.Value;
            return obj;
        }

        public static string HtmlEncode(this string str)
        {
            if (str == null)
                return null;
            return HttpUtility.HtmlEncode(str);
        }

        public static string HtmlDecode(this string str)
        {
            if (str == null)
                return null;
            return HttpUtility.HtmlDecode(str);
        }

        public static string UrlEncode(this string str)
        {
            if (str == null)
                return null;
            return HttpUtility.UrlEncode(str);
        }

        public static string UrlDecode(this string str)
        {
            if (str == null)
                return null;
            return HttpUtility.UrlDecode(str);
        }


        public static byte[] ReadAllBytes(this Stream source)
        {
            long originalPosition = source.Position;
            source.Position = 0;
            try {
                byte[] readBuffer = new byte[4096];

                int totalBytesRead = 0;
                int bytesRead = 0;

                while ((bytesRead = source.Read(readBuffer, totalBytesRead, readBuffer.Length - totalBytesRead)) > 0) {
                    totalBytesRead += bytesRead;

                    if (totalBytesRead == readBuffer.Length) {
                        int nextByte = source.ReadByte();
                        if (nextByte != -1) {
                            byte[] temp = new byte[readBuffer.Length * 2];
                            Buffer.BlockCopy(readBuffer, 0, temp, 0, readBuffer.Length);
                            Buffer.SetByte(temp, totalBytesRead, (byte)nextByte);
                            readBuffer = temp;
                            totalBytesRead++;
                        }
                    }
                }

                byte[] buffer = readBuffer;
                if (readBuffer.Length != totalBytesRead) {
                    buffer = new byte[totalBytesRead];
                    Buffer.BlockCopy(readBuffer, 0, buffer, 0, totalBytesRead);
                }
                return buffer;
            }
            finally {
                source.Position = originalPosition;
            }
        }

        public static void ThrowIfNull(this Object obj)
        {
            if (obj == null)
                throw new ArgumentNullException();
        }

        public static void ThrowIfNull(this Object obj, string paramName)
        {
            if (obj == null)
                throw new ArgumentNullException(paramName);
        }

        public static void Clear(this StringBuilder stringBuilder)
        {
            stringBuilder.Remove(0, stringBuilder.Length);
        }

        public static T? Process<T>(this T? value, Func<T, T?> isNotNull) where T : struct
        {
            return Process<T>(value, isNotNull, null);
        }

        public static T? Process<T>(this T? value, Func<T?> isNull) where T : struct
        {
            return Process<T>(value, null, isNull);
        }

        public static T? Process<T>(this T? value, Func<T, T?> isNotNull, Func<T?> isNull) where T : struct
        {
            if (value == null) {
                if (isNull != null)
                    return isNull();
                else
                    return null;
            }
            else {
                if (isNotNull != null)
                    return isNotNull(value.Value);
                else
                    return value;
            }
        }

        public static T Process<T>(this T value, Action<T> isNotNull) where T : class
        {
            return Process<T>(value, isNotNull, null);
        }

        public static T ProcessNull<T>(this T value, Func<T> isNull) where T : class
        {
            return Process<T>(value, null, isNull);
        }

        public static T Process<T>(this T value, Action<T> isNotNull, Func<T> isNull) where T : class
        {
            if (value == null) {
                if (isNull != null)
                    return isNull();
                else
                    return null;
            }
            else {
                if (isNotNull != null)
                    isNotNull(value);
                return value;
            }
        }

        public static void Action<T>(this T? value, Action<T> isNotNull) where T : struct
        {
            if (value != null && isNotNull != null)
                isNotNull(value.Value);
        }

        public static void Action<T>(this T value, Action<T> isNotNull) where T : class
        {
            if (value != null && isNotNull != null)
                isNotNull(value);
        }

        public static void Actions<T>(this IEnumerable<T> values, Action<T> action)
        {
            if (action != null) {
                foreach (var i in values) {
                    if (i != null)
                        action(i);
                }
            }
        }

        public static TTo Transform<TFrom, TTo>(this TFrom value, Func<TFrom, TTo> transform)
        {
            return transform(value);
        }

        public static TTo TransformN<TFrom, TTo>(this TFrom value, Func<TFrom, TTo> transform)
        {
            return TransformN(value, transform, default(TTo));
        }

        public static TTo TransformN<TFrom, TTo>(this TFrom value, Func<TFrom, TTo> transform, TTo ifNull)
        {
            if (value == null)
                return ifNull;
            return transform(value);
        }

        public static TTo TransformN<TFrom, TTo>(this TFrom? value, Func<TFrom, TTo> transform) where TFrom : struct
        {
            return TransformN(value, transform, default(TTo));
        }

        public static TTo TransformN<TFrom, TTo>(this TFrom? value, Func<TFrom, TTo> transform, TTo ifNull) where TFrom : struct
        {
            if (value == null)
                return ifNull;
            return transform(value.Value);
        }


        public static T CreateCopy<T>(this T obj)
        {
            if (obj == null)
                return obj;
            BinaryFormatter bf = new BinaryFormatter();
            using (MemoryStream ms = new MemoryStream()) {
                bf.Serialize(ms, obj);
                ms.Seek(0, SeekOrigin.Begin);
                return (T)bf.Deserialize(ms);
            }
        }


        public static TValue GetValueDef<TKey, TValue>(this Dictionary<TKey, TValue> dict, TKey key)
        {
            return GetValueDef(dict, key, default(TValue));
        }

        public static TValue GetValueDef<TKey, TValue>(this Dictionary<TKey, TValue> dict, TKey key, TValue notFound)
        {
            TValue val;
            if (dict.TryGetValue(key, out val))
                return val;
            return notFound;
        }

        public static TValue? GetValueDefN<TKey, TValue>(this Dictionary<TKey, TValue> dict, TKey key) where TValue : struct
        {
            TValue val;
            if (dict.TryGetValue(key, out val))
                return val;
            return null;
        }

        public static TValue AddSub<TKey, TValue>(this Dictionary<TKey, TValue> dict, TKey key, Action<TValue> action) where TValue : class
        {
            return AddSub(dict, key, null, action);
        }

        public static TValue AddSub<TKey, TValue>(this Dictionary<TKey, TValue> dict, TKey key, Func<TValue> create, Action<TValue> action) where TValue : class
        {
            TValue sub;
            if (!dict.TryGetValue(key, out sub)) {
                if (create != null)
                    sub = create();
                else
                    sub = Activator.CreateInstance<TValue>();
                dict.Add(key, sub);
            }
            if (action != null)
                action(sub);
            return sub;
        }

        public static TValue AddExistAction<TKey, TValue>(this Dictionary<TKey, TValue> dict, TKey key, TValue value, Func<TValue, TValue, TValue> action)
        {
            TValue sub;
            if (!dict.TryGetValue(key, out sub)) {
                dict.Add(key, value);
            }
            else {
                sub = action(sub, value);
                dict[key] = sub;
            }
            return sub;
        }


        public static IEnumerable<TResult> ConvertibleTo<TResult>(this IEnumerable source) where TResult : class
        {
            source.ThrowIfNull("source");
            foreach (var i in source) {
                TResult r = i as TResult;
                if (r != null)
                    yield return r;
            }
        }
    }
}
